using System;
using System.Collections ;
using System.Collections.Generic;
using IGlobalist.Chat.Engine.Common;
namespace IGlobalist.Chat.Engine
{
	public class Room : IDisposable
	{
		#region Members
        private string _ID;
        private string _Name;
        private User _Administrator;

        public User Administrator
        {
            get { return _Administrator; }
            set { _Administrator = value; }
        }
        private List<Message> _Messages = null;        
		private Dictionary<string,User> RoomUsers;
        private int userChatRoomSessionTimeout;		
		#endregion

        #region Properties
        public List<Message> Messages
        {
            get { return _Messages; }
            set { _Messages = value; }
        }
        

        public string ID
        {
            get { return _ID; }
            set { _ID = value; }
        }

        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }

       
        #endregion

        #region IDisposable Members
        public void Dispose()
		{
			_Messages.Clear();
			_ID="";
			foreach(object key in RoomUsers.Keys)
			{
				this.RoomUsers[key.ToString()].Dispose ();
			}
				
			
		}

		#endregion
		
		#region Constructors
		public Room(string roomID) 
		{
            _Messages = new List<Message>();
			_ID=roomID;
            userChatRoomSessionTimeout = Int32.Parse(System.Configuration.ConfigurationManager.AppSettings["UserChatRoomSessionTimeout"]);
            RoomUsers = new Dictionary<string,User>(Int32.Parse(System.Configuration.ConfigurationManager.AppSettings["ChatRoomMaxUsers"]));
		}
		#endregion

		#region Methods
		/// <summary>
		/// Returns the user with the specified id
		/// </summary>
		/// <param name="userID"></param>
		/// <returns></returns>
		public User GetUser(string userID)
		{
            if (!this.RoomUsers.ContainsKey(userID))
                return null;
			return this.RoomUsers[userID];
		}


        public void UpdateUser(User user)
        {
            if (!this.RoomUsers.ContainsKey(user.ID))
                this.RoomUsers[user.ID] = user;
        }


		/// <summary>
		/// Determines if the room is empty or not
		/// </summary>
		/// <returns></returns>
		public bool IsEmpty()
		{
			lock(this)
			{
				foreach(object key in RoomUsers.Keys)
				{
					if (this.RoomUsers[key.ToString()].IsActive==true)
						return false;
				}
				return true;
			}
		}

 
		#region Operations Join,Send,Leave
		/// <summary>
		/// Marks the user as inactive
		/// </summary>
		/// <param name="userID"></param>
		/// <returns></returns>
		public void LeaveRoom(string userID)
		{
			//deactivate user 
			User user=this.GetUser(userID);
            if (user == null)
                return ;
			user.IsActive=false;
			user.LastSeen=DateTime.Now;
            this.RoomUsers.Remove(userID);

			//Add leaving message
            Message msg = new Message(user.UserName, "has left the room", MsgType.Left);
            msg.Sender = user.UserName;
            
            if (user.Type == UserType.Admin)
            {
                msg.Receiver = "";
            }
            else
            {
                msg.Receiver = this.Administrator.UserName;
            }
			this.AddMsg(msg);
			
            if (IsEmpty())
                ChatEngine.DeleteRoom(this.ID);
			
		}


		/// <summary>
		/// Activates the user and adds a join message to the room
		/// </summary>
		/// <param name="userID"></param>
		/// <param name="userName"></param>
		/// <returns>All the messages sent in the room</returns>
		public string JoinRoom(string userID,string userName)
		{
				
			//activate user 
            
			User user=new User(userID,userName);
			user.IsActive=true;
			user.UserName=userName;
			user.LastSeen=DateTime.Now;
			if (!this.RoomUsers.ContainsKey(userID))
			{
				//Add join message
				Message msg=new Message(user.UserName ,"",MsgType.Join);
				this.AddMsg(msg);
				//Get all the messages to the user
				int lastMsgID;
                List<Message> previousMessages = this.GetMessagesSince(-1, out lastMsgID);
				user.LastMessageReceived=lastMsgID;
				//return the messages to the user
				string str=GenerateMessagesString(previousMessages);
				this.RoomUsers.Add(userID,user);
				return str;
			}
			return "";
		}

        /// <summary>
        /// Activates the user and adds a join message to the room
        /// </summary>
        /// <param name="User"></param>
       
        /// <returns>All the messages sent in the room</returns>
        public void JoinRoom(User user)
        {
            //activate user 
            user.IsActive = true;         
            user.LastSeen = DateTime.Now;
            
            if (!this.RoomUsers.ContainsKey(user.ID))
            {
                //Add join message
                Message msg = new Message(user.UserName, "has joined the room!", MsgType.Join);
                msg.Sender = user.UserName;
                msg.Receiver = this.Administrator.UserName;
                
                this.AddMsg(msg);
                ////Get all the messages to the user
                int lastMsgID=-1;
                //List<Message> previousMessages = this.GetMessagesSince(-1, out lastMsgID);
                //user.LastMessageReceived = lastMsgID;
                //////return the messages to the user
                //string[][] str = GenerateMessagesToArray(previousMessages);
                user.LastMessageReceived = lastMsgID;
                this.RoomUsers.Add(user.ID, user);
                //return  str;
            }
            //return new string[0][];
        }

        



        /// <summary>
        /// Adds a message in the room
        /// </summary>
        /// <param name="strMsg"></param>
        /// <param name="senderID"></param>
        /// <returns>All the messages sent from the other user from the last time the user sent a message</returns>
        public Message[] SendMessage(string strMsg, string senderID, string receiver, MsgType type)
        {
            User user = this.GetUser(senderID);            
            Message msg = new Message(user.UserName, strMsg, type);
            msg.Sender = user.UserName;
            msg.Receiver = receiver;
            
            user.LastSeen = DateTime.Now;
            this.ExpireUsers(userChatRoomSessionTimeout);
            this.AddMsg(msg);

            Message[] res = null;
            if (user.Type == UserType.Admin && type == MsgType.Private)
            {
                res=new Message[0];
            }
            else
            {
                int lastMsgID;
                List<Message> previousMsgs = this.GetMessagesSince(user.LastMessageReceived, out lastMsgID);
                previousMsgs = GetMessageOfUser(previousMsgs, user);
                if (lastMsgID != -1)
                    user.LastMessageReceived = lastMsgID;
                res = this.ConvertList2Array(previousMsgs);
            }
            return res;
        }

       

        private Message[] ConvertList2Array(List<Message> list)
        {
            Message[] msgs = new Message[list.Count];
            for (int i=0;i<list.Count;i++)
            {
                msgs[i] = list[i];
            }
            return msgs;
        }

        private List<Message> GetMessageOfUser(List<Message> list, User user)
        {
            if (user.Type != UserType.Admin)
            {
                int i = 0;
                while (i < list.Count)
                {
                    if (list[i].Receiver == user.UserName || list[i].Receiver == "" || list[i].Sender==user.UserName)
                    {
                        i++;
                    }
                    else
                    {
                        list.RemoveAt(i);
                    }
                }
            }
            return list;
        }

		#endregion 

		/// <summary>
		/// Removes the users that hasn't sent any message during the last window secondes 
		/// </summary>
		/// <param name="window">time in secondes</param>
		public void ExpireUsers(int window) 
		{
			lock(this) 
			{
                foreach (object key in RoomUsers.Keys)
                {
                    User usr = this.RoomUsers[key.ToString()];
                    lock (usr)
                    {
                        if (usr.LastSeen != System.DateTime.MinValue)
                        {
                            TimeSpan span = DateTime.Now - usr.LastSeen;
                            if (span.TotalSeconds > window && usr.IsActive != false)
                            {
                                this.LeaveRoom(usr.ID);
                            }
                        }
                    }
                }
			}
		}


		/// <summary>
		/// Adds a message to the room
		/// <param name="msg"></param>
		/// <returns> the id of the new message</returns>
		public int AddMsg(Message msg) 
		{
			int count;
			lock(_Messages) 
			{
                count = _Messages.Count;
                _Messages.Add(msg);
			}
			return count;
		}



		/// <summary>
		/// Iterates over the messages array calling ToString() for each message
		/// </summary>
		/// <param name="msgs"></param>
		/// <returns></returns>
        private string GenerateMessagesString(List<Message> msgs)
		{
			string res="";
			for (int i=0;i<msgs.Count;i++)
			{
				res+=((Message)msgs[i]).ToString()+"<br/>"; 
			}
			return res;
		}


        /// <summary>
        /// Iterates over the messages array calling ToString() for each message
        /// </summary>
        /// <param name="msgs"></param>
        /// <returns></returns>
        private string[][] GenerateMessagesToArray(List<Message> msgs)
        {
            string[][] arrs = new string[msgs.Count][];
            string res = "";
            for (int i = 0; i < msgs.Count; i++)
            {                
                arrs[i] = new string[2]{((Message)msgs[i]).ToString() + "<br/>",msgs[i].Sender};                               
            }
            return arrs;
        }

		
		/// <summary>
		/// Returns an array that contains all messages sent after the message with id=msgid
		/// </summary>
		/// <param name="msgid">The id of the message after which all the message will be retuned </param>
		/// <param name="lastMsgID">the id of the last message returned</param>
		/// <returns></returns>
		public List<Message> GetMessagesSince(int msgid,out int lastMsgID) 
		{
			lock(_Messages) 
			{
                if ((_Messages.Count) <= (msgid + 1))
					lastMsgID=-1;
				else
                    lastMsgID = _Messages.Count - 1;
                return _Messages.GetRange(msgid + 1, _Messages.Count - (msgid + 1));
			}
		}






		/// <summary>
		/// Returns all the messages sent since the last message the user received
		/// </summary>
		/// <param name="userID"></param>
		/// <returns></returns>
		public Message[] UpdateUser(string userID)
		{
			User user=this.GetUser(userID);
			user.LastSeen=DateTime.Now;
			this.ExpireUsers(userChatRoomSessionTimeout);
			int lastMsgID;
			List<Message> previousMsgs= this.GetMessagesSince( user.LastMessageReceived,out lastMsgID);
            previousMsgs = GetMessageOfUser(previousMsgs, user);
			if (lastMsgID!=-1)
				user.LastMessageReceived=lastMsgID;
            return this.ConvertList2Array(previousMsgs);			
		}



        /// <summary>
        /// Returns the names of the users who aer currently in the rooms
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> GetRoomUsersNames()
        {
            ExpireUsers(userChatRoomSessionTimeout);
            foreach (object key in RoomUsers.Keys)
            {
                yield return  this.RoomUsers[key.ToString()].UserName ;
            }

        }

		#endregion 

        


        

    }

}
